package org.meb.conquest.web.ctrl;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.PostConstruct;
import javax.faces.model.SelectItem;
import javax.faces.view.ViewScoped;
import javax.inject.Inject;
import javax.inject.Named;

import lombok.Getter;
import lombok.Setter;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.collections4.Predicate;
import org.apache.commons.collections4.functors.AndPredicate;
import org.apache.commons.lang3.ObjectUtils;
import org.meb.conquest.db.converter.CardTypeConverter;
import org.meb.conquest.db.dao.Query;
import org.meb.conquest.db.model.CardType;
import org.meb.conquest.db.model.Deck;
import org.meb.conquest.db.model.DeckMember;
import org.meb.conquest.db.model.Faction;
import org.meb.conquest.db.model.loc.Card;
import org.meb.conquest.db.model.loc.Domain;
import org.meb.conquest.db.query.CardQuery;
import org.meb.conquest.db.util.Transformers;
import org.meb.conquest.web.auth.WebUser;
import org.meb.conquest.web.model.CardGroup;
import org.meb.conquest.web.model.CardItem;
import org.meb.conquest.web.services.CardService;
import org.meb.conquest.web.services.DeckService;
import org.meb.conquest.web.services.SearchService;

@Named
@ViewScoped
@Getter
@Setter
public class DeckBuildCtrl implements Serializable {

	private static final long serialVersionUID = -4957465875046700679L;

	@Inject
	private SearchService searchService;

	@Inject
	private CardService cardService;

	@Inject
	private DeckService deckService;
	
	@Inject
	private WebUser webUser;

	private List<CardItem> warlords;
	private List<CardItem> deckCards;
	private List<CardItem> filteredDeckCards;
	private Deck deck;
	private Long deckId;

	private List<SelectItem> quantitySelectItems;
	private List<SelectItem> cardTypeSelectItems;
	private List<String> selectedCardTypes;
	private List<SelectItem> factionSelectItems;
	private List<String> selectedFactions;
	
	private CardGroup cardGroup;

	private Object styleClass;

	@PostConstruct
	public void initialize() {
		deck = null;
		deckId = null;
		deckCards = null;
		filteredDeckCards = null;

		if (warlords == null) {
			Query<Card> query = new Query<Card>(new Card());
			query.getExample().setType(CardType.WARLORD);
			query.getSorting().setSortingAsc("factionDisplay");
			query.getSorting().setSortingAsc("number");
			List<Card> cards = searchService.find(query);
			warlords = new ArrayList<CardItem>();
			for (Card card : cards) {
				warlords.add(new CardItem(card));
			}
		}

		quantitySelectItems = new ArrayList<SelectItem>();
		for (int i = 0; i < 4; i++) {
			quantitySelectItems.add(new SelectItem(new Integer(i)));
		}

		HashMap<String, String> domainsMap = new HashMap<String, String>();
		List<Domain> domains = searchService.find(new Domain());
		for (Domain domain : domains) {
			domainsMap.put(domain.getValue(), domain.getDescription());
		}

		CardTypeConverter tmpConverter = new CardTypeConverter();
		cardTypeSelectItems = new ArrayList<SelectItem>();
		selectedCardTypes = new ArrayList<String>();
		for (CardType cardType : new CardType[] { CardType.ARMY, CardType.ATTACHMENT, CardType.SUPPORT, CardType.EVENT }) {
			String tmp = tmpConverter.convertToDatabaseColumn(cardType);
			cardTypeSelectItems.add(new SelectItem(cardType.toString(), domainsMap.get(tmp).substring(0, 1)));
			selectedCardTypes.add(cardType.toString());
		}
	}

	public boolean isSelectWarlordMode() {
		return deck == null;
	}

	public boolean isBuildDeckMode() {
		return deck != null;
	}

	public void selectWarlord() {
		deck = null;
		deckId = null;
		deckCards = null;
		filteredDeckCards = null;
	}

	public void initBuildDeck() {
		if (deckId == null) {
			throw new IllegalStateException("Deck id is null");
		}
		// TODO check deck permissions
		deck = searchService.findUnique(new Deck(deckId));
		buildDeck();
	}

	public void initBuildDeck(Card warlord) {
		deck = new Deck();
		deck.setWarlord(warlord);
		buildDeck();
	}

	private void buildDeck() {

		Faction faction = deck.getWarlord().getFaction();
		switch (faction) {
			case ASTRA_MILITARUM:
				styleClass = "f-alliance-o-am-sm";
				break;
			case SPACE_MARINES:
				styleClass = "f-alliance-am-sm-t";
				break;
			case TAU:
				styleClass = "f-alliance-sm-t-e";
				break;
			case ELDAR:
				styleClass = "f-alliance-t-e-de";
				break;
			case DARK_ELDAR:
				styleClass = "f-alliance-e-de-c";
				break;
			case CHAOS:
				styleClass = "f-alliance-de-c-o";
				break;
			case ORK:
				styleClass = "f-alliance-c-o-am";
				break;
			default:
				throw new IllegalStateException();
		}

		factionSelectItems = new ArrayList<SelectItem>();
		selectedFactions = new ArrayList<String>();
		for (Faction f : faction.alliance()) {
			factionSelectItems.add(new SelectItem(f.toString(), ""));
			selectedFactions.add(f.toString());
		}

		CardQuery query = new CardQuery();
		query.getSorting().setSortingAsc("factionDisplay");
		query.getSorting().setSortingAsc("typeDisplay");
		query.getSorting().setSortingAsc("name");
		query.setDeckFaction(faction);
		query.setDeckWarlordId(deck.getWarlord().getId());
		List<Card> cards = cardService.findCards(query);

		Map<Long, DeckMember> memberByCardId = new HashMap<>();
		MapUtils.populateMap(memberByCardId, deck.getDeckMembers(), Transformers.DEME_CARD_ID);

		deckCards = new ArrayList<CardItem>();
		for (Card card : cards) {
			CardItem item = new CardItem(card);
			if (memberByCardId.containsKey(card.getId())) {
				item.setQuantity(memberByCardId.get(card.getId()).getQuantity());
			}
			deckCards.add(item);
		}
		
		cardGroup = new CardGroup();
		cardGroup.groupByType(deckCards);

		filterDeckCards();
	}

	public void saveDeck() {
		Set<DeckMember> members = deck.getDeckMembers();
		if (members == null) {
			members = new HashSet<DeckMember>();
		}
		Map<Long, DeckMember> memberByCardId = new HashMap<>();
		MapUtils.populateMap(memberByCardId, deck.getDeckMembers(), Transformers.DEME_CARD_ID);

		for (CardItem item : deckCards) {
			Card card = item.getCard();
			Long cardId = card.getId();
			Integer quantity = item.getQuantity();
			if (quantity == 0 && memberByCardId.containsKey(cardId)) {
				deck.getDeckMembers().remove(memberByCardId.get(cardId));
			} else if (quantity > 0) {
				if (memberByCardId.containsKey(cardId)) {
					memberByCardId.get(cardId).setQuantity(quantity);
				} else {
					DeckMember link = new DeckMember();
					link.setCard(card);
					link.setDeck(deck);
					link.setQuantity(quantity);
					deck.getDeckMembers().add(link);
				}
			}
		}
		

		Date date = new Date();
		if (deck.getId() == null) {
			deck.setCreateDate(date);
		}
		deck.setModifyDate(date);
		deck.setUserId(webUser.getUserId());
		
		deckService.saveDeck(deck);
	}

	public void saveDeckAsCopy() {

	}

	public void filterDeckCards() {
		filteredDeckCards = CollectionUtils.select(deckCards, AndPredicate.andPredicate(new Predicate<CardItem>() {

			@Override
			public boolean evaluate(CardItem cardItem) {
				return selectedCardTypes.contains(cardItem.getCard().getType().toString());
			}

		}, new Predicate<CardItem>() {

			@Override
			public boolean evaluate(CardItem cardItem) {
				return selectedFactions.contains(cardItem.getCard().getFaction().toString());
			}

		}), new ArrayList<CardItem>());
	}

	public int sortByCardTotalCost(Object obj0, Object obj1) {
		Card card0 = (Card) obj0;
		Card card1 = (Card) obj1;
		return ObjectUtils.compare(card0.getCost(), card1.getCost(), true);
	}

	public void cardUpdated(CardItem item) {
		System.out.println();
	}
}
